package com.rhythm.commons.net.messaging;

import com.rhythm.base.Strings;
import com.rhythm.commons.net.EmailPasswordAuthenticator;
import com.rhythm.commons.net.NetResponse;
import com.rhythm.commons.net.NetUtil;
import com.rhythm.commons.validation.InvalidStateException;
import com.sun.mail.smtp.SMTPMessage;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.Session;
import javax.mail.Transport;

/**
 * Class provides a simple implementation of a SMTP messaging client
 * <p>
 * <strong>Implementation Example</strong>
 * <pre>
 * SimpleSMTPClient client = SimpleSMTPClient.of("mail.example.com");
 * SimpleSMTPMessage message = client.createMessage("user", "password");
 * message.setSender(user@example.com");
 * message.setRecepient("recepient@example.com");
 * message.setSubject("Hello World");
 * message.setBody("Hello. How are you?");
 * client.send(message);
 * </pre>
 * </p>
 * @author Michael J. Lee
 */
public class SimpleSMTPClient {

    public static final String DEFAULT_SMTP_PORT = "25";

    public static final String DEFAULT_USER = "";

    public static final String PROPS_SMTP_HOST = "mail.smtp.host";

    private static final int DEFAULT_TIMEOUT = 10000;

    private Properties props;

    private final String host;

    private final String port;

    /**
     * Creates a new instance of a {@code SMTPCient} for the
     * given host. E.g. 'mail.hostname.com'
     * @param host a valid host name
     * @return a new instance of a {@code SMTPCient}
     */
    public static SimpleSMTPClient of(final String host) {
        return new SimpleSMTPClient(host, DEFAULT_SMTP_PORT, DEFAULT_USER);
    }

    /**
     * Creates a new instance of a {@code SMTPCient} for the
     * given host. E.g. 'mail.hostname.com'
     * @param host a valid host name
     * @return a new instance of a {@code SMTPCient}
     */
    public static SimpleSMTPClient of(final String host, final String port) {
        return new SimpleSMTPClient(host, port, DEFAULT_USER);
    }

    /**
     * Creates a new instance of a {@code SMTPCient} for the
     * given host. E.g. 'mail.hostname.com'
     * @param host a valid host name
     * @return a new instance of a {@code SMTPCient}
     */
    public static SimpleSMTPClient of(final String host, final String port, final String user) {
        return new SimpleSMTPClient(host, port, user);
    }

    private SimpleSMTPClient(final String host, String port, String user) {
        this.host = host;
        this.port = port;
        createMailServerSettings(host, user, port);

    }

    public SimpleSMTPMessage createMessage(String username, String password) {
        Authenticator authenticator = new EmailPasswordAuthenticator(username, password);
        createMailServerSettings(host, username, port);
        return new SimpleSMTPMessage(Session.getInstance(this.props, authenticator));
    }

    /**
     * Attempts to send a instance of a {@code SimpleSMTPMessage}
     * 
     * @param emailMessage a valid instance of a {@code SimpleSMTPMessage}
     * @throws InvalidStateException
     * @throws MessagingException
     */
    public void send(SimpleSMTPMessage emailMessage) throws InvalidStateException, MessagingException {

        emailMessage.checkState(new SimpleSMTPMessageValidator());

        Message message = new SMTPMessage(emailMessage.getSession());

        message.setFrom(new InternetAddress(emailMessage.getSender()));
        message.addRecipient(Message.RecipientType.TO, new InternetAddress(emailMessage.getRecepient()));
        message.setSubject(emailMessage.getSubject());
        message.setText(emailMessage.getBody());

        Transport.send(message);
    }

    private void createMailServerSettings(String mailHost, String user, String port) {
        this.props = System.getProperties();
        this.props.put(PROPS_SMTP_HOST, mailHost);

        if (!Strings.isEmptyOrNull(user)) {
            this.props.put("mail.smtp.submitter", user);
            this.props.put("mail.smtp.auth", "true");
        } else {
            this.props.put("mail.smtp.auth", "false");
        }

        if (!Strings.isEmptyOrNull(port)) {
            this.props.put("mail.smtp.port", port);
        }


    }

    /**
     * Attempts to connect to the provided smtp host.
     * @return
     * @see NetUtil.ping(host, timeout)
     * @see NetResponse
     */
    public boolean tryConnect() {
        return NetUtil.ping(host, DEFAULT_TIMEOUT).wasSuccessful();
    }
}
